#include "iic.h"



static void IICStart(void);			//发送iic开始信号
static void IICStop(void);	  		//发送iic停止信号
static void IICAck(void);			//iic发送ACK信号
static void IICNAck(void);			//iic不发送ACK信号 
static u8 IICWaitAck(void);			//iic等待ACK信号
static void IICSendByte(u8 txd);	//iic发送一个字节
static u8 IICReceiveByte(u8 ack);	//iic读取一个字节

/**
 * @Description 软件IIC初始化400KHZ、用于与激光传感器vl53通信
 */
void SoftWare_IIC_Init(void)
{	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/*使能VL53时钟*/
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	/*SCL PA12   SDA PA11*/
	GPIO_InitStructure.GPIO_Pin = IIC_SCL_PIN | IIC_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;		//开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	//外部已经有上拉
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA,IIC_SCL_PIN);	//PA11输出高
	GPIO_SetBits(GPIOA,IIC_SDA_PIN);	//PA12输出高
}

/**
* @Description IIC产生开始信号
 */
static void IICStart(void)
{
	SDA_OUT();     	//sda线输出
	IIC_SCL=0;	  	//拉低时钟线，表示开始传输信号
	delay_us(1);
	IIC_SDA=1;	  	//先将数据线拉高	  
	delay_us(1);
	IIC_SCL=1;		//将时钟线拉高
	delay_us(4);
 	IIC_SDA=0;		//在时钟线高电平时，拉低数据线，表示一个IIC起始信号
	delay_us(4);
	IIC_SCL=0;		//拉低时钟线、准备往数据线传输数据
}


/**
* @Description IIC产生停止信号
 */	
static void IICStop(void)
{
	
	SDA_OUT();		//sda线输出
	IIC_SCL=0;
	delay_us(1);
	IIC_SDA=0;		//先将SDA拉低
 	delay_us(4);
	IIC_SCL=1;		//再将SCL拉高
	delay_us(4);	
	IIC_SDA=1;		//在SCL为高电平期间，将SDA由低电平设为高电平，表示IIC结束信号
	delay_us(4);
}



/**
 * @Description IIC 等待应答信号到来
 * 返回值：1，接收应答失败； 0，接收应答成功
 */
static u8 IICWaitAck(void)
{
	u8 ucErrTime=0;
	SDA_IN();      			//SDA设置为输入  
	IIC_SDA=1;delay_us(1);	//释放SDA线   
	IIC_SCL=1;delay_us(1);	//释放SCL线 
	//等待从机拉低SDA，表示ack
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			//超时未ack，结束传输
			IICStop();
			return 1;
		}
	}
	IIC_SCL=0;				//收到应答，拉低SCL，准备传输数据	   
	return 0;  
} 



/**
* @Description IIC产生应答
 */	  
static void IICAck(void)
{
	IIC_SCL=0;		//拉低SCL以便设置SDA
	SDA_OUT();
	IIC_SDA=0;		//拉低SDA表示应答
	delay_us(1);
	IIC_SCL=1;		//拉高SCL,让从机读取SDA上的电平
	delay_us(1);
	IIC_SCL=0;
}



/**
* @Description IIC不产生应答
 */	  	    
static void IICNAck(void)
{
	IIC_SCL=0;		//拉低SCL以便设置SDA
	SDA_OUT();
	IIC_SDA=1;		//SDA拉高表示不应答
	delay_us(1);
	IIC_SCL=1;		//拉高SCL,让从机读取SDA上的电平
	delay_us(1);
	IIC_SCL=0;
}		


/**
 * @Description IIC发送一个字节
 */	  
static void IICSendByte(u8 txd)
{                        
    u8 t;   
	SDA_OUT();			//数据线准备传输数据 	    
    IIC_SCL=0;			//拉低时钟线开始数据传输
	
	//从高到低 发送8bit的数据
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(1);   
		IIC_SCL=1;		//拉高时钟线，保持数据线不变，使从机能读取SDA的电平
		delay_us(1); 
		IIC_SCL=0;		//拉低信号线，准备传输下一bit
		delay_us(1);
    }	 
} 	   



/**
 * @Description IIC读取一个字节
 * need_ack:是否需要ack
 */	   
static u8 IICReceiveByte(u8 need_ack)
{
	u8 i,receive=0;
	SDA_IN();			//SDA设置为输入
	
	//从高到低 读取8bit数据
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 		//拉低SCL,让从机设置SDA的电平
        delay_us(1);
		IIC_SCL=1;		//拉高SCL,准备读取SDA电平
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!need_ack)
        IICNAck();		//发送nACK
    else
        IICAck(); 		//发送ACK   
    return receive;
}


/**
 * @Description 读取指定IIC设备指定地址范围的值
 * rbuf：读取数据缓存
 */	 
void IIC_ReadBytes(u8 devaddr,u16 addr,u8 len,u8 *rbuf)
{
	int res;
	int i=0;
	
	IICStart(); 			//发送IIC起始信号
	IICSendByte(devaddr);	//发送设备地址
	res = IICWaitAck();		//等待设备应答
	if(res)
		return;
	
	IICSendByte(addr>>8);  	//发送数据的高位地址
	res = IICWaitAck();
	if(res)
		return;
	
	IICSendByte(addr&0xFF);  //发送数据的低位地址
	res = IICWaitAck();	
	if(res)
		return ;
	
	//等待从设备完成数据准备工作
	IICStart();  			 //发送IIC起始信号
	IICSendByte(devaddr|1);	 //发送设备地址,bit0 为1表示读数据  	
	res = IICWaitAck();	
	if(res)
		return ;
	
	//读取len个字节的数据
	for(i=0; i<len; i++)
	{
		if(i==len-1)
		{
			rbuf[i] = IICReceiveByte(0);	//最后一个字节不应答
		}
		else
			rbuf[i] = IICReceiveByte(1);	//应答
	}
	IICStop( );		//结束传输
}


/**
 * @Description 连续写多个字节
 * wbuf:写数据缓存
 */	 
void IIC_WriteBytes(u8 devaddr,u16 addr,u8 len,u8 *wbuf)
{
	int i=0;
	int res;
	IICStart();  			//发送IIC起始信号
	IICSendByte(devaddr);  	//发送设备地址
	res = IICWaitAck();	
	if(res)
		return ;
	
	IICSendByte(addr>>8);  	//发送写入的高位地址
	res = IICWaitAck();	
	if(res)
		return ;
	
	IICSendByte(addr&0xFF);  //发送写入的低位地址
	res = IICWaitAck();	
	if(res)
		return ;
	
	//发送len个字节数据
	for(i=0; i<len; i++)
	{
		IICSendByte(wbuf[i]);  
		res = IICWaitAck();	
		if(res)
			break ;		
	}
	IICStop( );	
}
